﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using P3SS.Settings;
using System.Management;
using System.Linq;
using System.Diagnostics;
using System.ServiceProcess;
using P3SS.Engine;
using P3SS.FailedMail;
using P3SS.IPC;

namespace P3SS.ConfigUtil
{
	public partial class frmMain : Form
	{
		private ServiceSettings mySettings;
		private bool isSaved;
		private BackgroundWorker bgwUpdateEvents;

		public frmMain()
		{
			InitializeComponent();
			bgwUpdateEvents = new BackgroundWorker();
			bgwUpdateEvents.DoWork += new DoWorkEventHandler(bgwUpdateEvents_DoWork);
			bgwUpdateEvents.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgwUpdateEvents_RunWorkerCompleted);
		}

		private void frmMain_Load(object sender, EventArgs e)
		{
			bool canSeeService = updateServiceStatus();
			loadSettings();

			tmrEventsRefresh_Tick(this, new EventArgs());
		}

		private void markUnsaved()
		{
			isSaved = false;
			btnSave.Enabled = true;
		}

		private void saveSettings()
		{
			mySettings.MinutesBetweenChecks = (int)nudGeneralMinutesBetween.Value;
			mySettings.ShowDebugMessages = chkGeneralDebugMessages.Checked;

			ServiceSettings.SaveSettings(mySettings);
			isSaved = true;
			btnSave.Enabled = false;

			if ((updateServiceStatus()) && (srvcController.Status == ServiceControllerStatus.Running))
			{
				if (MessageBox.Show("Restart service now to update its settings?\r\nNOTE: Service will update its settings when it next collects mail", "Restart service?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
				{
					changeServiceStatus(ServiceAction.Restart);
				}
			}
		}

		private void loadSettings()
		{
			mySettings = ServiceSettings.LoadSettings();
			chkGeneralDebugMessages.Checked = mySettings.ShowDebugMessages;
			nudGeneralMinutesBetween.Value = mySettings.MinutesBetweenChecks;

			refreshAccountsListing();
		
			btnSave.Enabled = false;
			isSaved = true;
		}

		private void lstAccounts_SelectedIndexChanged(object sender, EventArgs e)
		{
			switch (lstAccounts.SelectedIndices.Count)
			{
				case 0:
					btnAccountDelete.Enabled = false;
					btnAccountDuplicate.Enabled = false;
					btnAccountEdit.Enabled = false;
					btnAccountDefaultMake.Enabled = false;
					break;
				case 1:
					btnAccountDelete.Enabled = true;
					btnAccountDuplicate.Enabled = true;
					btnAccountEdit.Enabled = true;
					btnAccountDefaultMake.Enabled = true;
					break;
				default:
					btnAccountDelete.Enabled = true;
					btnAccountDuplicate.Enabled = false;
					btnAccountEdit.Enabled = false;
					btnAccountDefaultMake.Enabled = false;
					break;
			}
		}

		private void btnAccountAdd_Click(object sender, EventArgs e)
		{
			frmAccount newAccountForm = new frmAccount(new Account(), false);

			if (newAccountForm.ShowDialog() == DialogResult.OK)
			{
				mySettings.Accounts.Add(newAccountForm.MyAccount);
				lstAccounts.Items.Add(newAccountForm.MyAccount);
				markUnsaved();
			}

			newAccountForm.Dispose();
		}

		private void btnAccountDuplicate_Click(object sender, EventArgs e)
		{
			if (lstAccounts.SelectedIndices.Count == 1)
			{
				frmAccount newAccountForm = new frmAccount(new Account((Account)lstAccounts.SelectedItem), false);

				if (newAccountForm.ShowDialog() == DialogResult.OK)
				{
					mySettings.Accounts.Add(newAccountForm.MyAccount);
					lstAccounts.Items.Add(newAccountForm.MyAccount);
					markUnsaved();
				}

				newAccountForm.Dispose();
			}
		}

		private void btnAccountDelete_Click(object sender, EventArgs e)
		{
			if ((lstAccounts.SelectedIndices.Count > 0) && (MessageBox.Show("Are you sure you wish to delete these accounts?", "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes))
			{
				mySettings.Accounts.RemoveAll(p => lstAccounts.SelectedItems.Contains(p));
				lstAccounts.Items.Clear();
				lstAccounts.Items.AddRange(mySettings.Accounts.ToArray());
				markUnsaved();
			}
		}

		private void btnAccountEdit_Click(object sender, EventArgs e)
		{
			if (lstAccounts.SelectedIndices.Count == 1)
			{
				frmAccount editAccountForm = new frmAccount(new Account((Account)lstAccounts.SelectedItem), false);

				if (editAccountForm.ShowDialog() == DialogResult.OK)
				{
					mySettings.Accounts.Remove((Account)lstAccounts.SelectedItem);
					mySettings.Accounts.Add(editAccountForm.MyAccount);
					lstAccounts.Items.Remove(lstAccounts.SelectedItem);
					lstAccounts.Items.Add(editAccountForm.MyAccount);
					lstAccounts.SelectedItem = editAccountForm.MyAccount;
					markUnsaved();
				}

				editAccountForm.Dispose();
			}
		}

		private void btnExit_Click(object sender, EventArgs e)
		{
			bool cancelExit = false;

			if (!isSaved)
			{
				DialogResult saveResult = MessageBox.Show("Save before exiting?", "Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

				if (saveResult == DialogResult.Yes)
				{
					saveSettings();
				}
				else if (saveResult == DialogResult.Cancel)
				{
					cancelExit = true;
				}
			}

			if ((!cancelExit) && (updateServiceStatus()) && (srvcController.Status == ServiceControllerStatus.Stopped))
			{
				DialogResult startServiceResult = MessageBox.Show("Start the service before exiting?", "Start service?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

				if (startServiceResult == DialogResult.Yes)
				{
					changeServiceStatus(ServiceAction.Start);
				}
				else if (startServiceResult == DialogResult.Cancel)
				{
					cancelExit = true;
				}
			}

			if (!cancelExit)
			{
				Application.Exit();
			}
		}

		private void btnRevert_Click(object sender, EventArgs e)
		{
			if (MessageBox.Show("This will reload the settings are revert/lose any changes made.\r\nContinue?", "Revert changes", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
			{
				loadSettings();
			}
		}

		private void btnSave_Click(object sender, EventArgs e)
		{
			saveSettings();
		}

		private void nudGeneralMinutesBetween_ValueChanged(object sender, EventArgs e)
		{
			markUnsaved();
		}

		private void chkGeneralDebugMessages_CheckedChanged(object sender, EventArgs e)
		{
			markUnsaved();
		}

		private void btnStatusStart_Click(object sender, EventArgs e)
		{
			changeServiceStatus(ServiceAction.Start);
		}

		private void btnStatusRestart_Click(object sender, EventArgs e)
		{
			changeServiceStatus(ServiceAction.Restart);
		}

		private void btnStatusStop_Click(object sender, EventArgs e)
		{
			changeServiceStatus(ServiceAction.Stop);
		}

		private void changeServiceStatus(ServiceAction inStatus)
		{
			frmChangeStatus changeStatus = new frmChangeStatus(inStatus);
			DialogResult result = changeStatus.ShowDialog();
			changeStatus.Dispose();

			updateServiceStatus();
			if (result == DialogResult.No)
			{
				MessageBox.Show("Unable to change the service's state.\r\nPlease ensure that you are running this as administrator.", "Failed to change state", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private bool updateServiceStatus()
		{
			bool canContactServer = true;

			try
			{
				srvcController.Refresh();
				lblStatusStatus.Text = srvcController.Status.ToString();

				switch (srvcController.Status)
				{
					case ServiceControllerStatus.Paused:
						btnStatusStart.Enabled = true;
						btnStatusStop.Enabled = true;
						btnCheckMail.Enabled = false;
						break;
					case ServiceControllerStatus.Running:
						btnStatusStart.Enabled = false;
						btnStatusStop.Enabled = true;
						btnCheckMail.Enabled = true;
						break;
					case ServiceControllerStatus.Stopped:
						btnStatusStart.Enabled = true;
						btnStatusStop.Enabled = false;
						btnCheckMail.Enabled = false;
						break;
				}
			}
			catch (Exception ex)
			{
				canContactServer = false;
				if (ex.InnerException is Win32Exception)
				{
					lblStatusStatus.Text = "Could not connect to service";
					btnStatusRestart.Enabled = false;
					btnStatusStart.Enabled = false;
					btnStatusStop.Enabled = false;
					btnCheckMail.Enabled = false;
				}
				else
				{
					MessageBox.Show("Unable to get the service's state.\r\nPlease ensure that you are running this as administrator.", "Failed to get state", MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}

			return canContactServer;
		}

		private void tmrCheckServiceStatus_Tick(object sender, EventArgs e)
		{
			updateServiceStatus();
		}

		private void lstAccounts_DoubleClick(object sender, EventArgs e)
		{
			btnAccountEdit_Click(sender, new EventArgs());
		}

		private void chkEventsRefresh_CheckedChanged(object sender, EventArgs e)
		{
			tmrEventsRefresh.Enabled = chkEventsRefresh.Checked;
		}

		private void tmrEventsRefresh_Tick(object sender, EventArgs e)
		{
			if (!bgwUpdateEvents.IsBusy)
			{
				pgbEventsLoading.Visible = true;
				lstEventsList.Visible = false;
				btnEventsRefreshNow.Enabled = false;
				bgwUpdateEvents.RunWorkerAsync(nudLimitMessages.Value);
			}
		}

		void bgwUpdateEvents_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (!e.Cancelled)
			{
				lstEventsList.Items.Clear();
				lstEventsList.Items.AddRange((ListViewItem[])e.Result);
				lstEventsList.Sort();

				pgbEventsLoading.Visible = false;
				lstEventsList.Visible = true;
				btnEventsRefreshNow.Enabled = true;
			}
		}

		void bgwUpdateEvents_DoWork(object sender, DoWorkEventArgs e)
		{
			List<ListViewItem> output = new List<ListViewItem>();
			int limit = Convert.ToInt32(e.Argument);

			var eventsQuery = (from EventLogEntry t in eventLogMain.Entries
							   where (t.Source == "P3SS")
							   orderby t.TimeGenerated descending
							   select new { Date=t.TimeGenerated, Source=t.CategoryNumber, t.Message, ErrorCode=Convert.ToInt32(t.InstanceId)}).Take(limit);
						
			foreach (var thisEvent in eventsQuery)
			{
				ListViewItem thisItem = new ListViewItem(new string[] { thisEvent.Date.ToString("yyyy-MM-dd HH:mm:ss"), ((EventLogCategory)thisEvent.Source).ToString(), thisEvent.Message }, getEventImageFromErrorCode(thisEvent.ErrorCode));
				output.Add(thisItem);
			}

			e.Result = output.ToArray();
		}

		private string getEventImageFromErrorCode(int errorCode)
		{
			string output = "event_unknown.png";
			MessageType errorType = EngineMessageEventArgs.typeFromErrorCode(errorCode);

			switch (errorType)
			{
				case MessageType.Information:
					output = "event_information.png";
					break;
				case MessageType.Warning:
					output = "event_warning.png";
					break;
				case MessageType.Error:
					output = "event_error.png";
					break;
				case MessageType.Debug:
					output = "event_debug.png";
					break;
				default:
					output = "event_unknown.png";
					break;
			}

			return output;
		}

		private void nudEventsRefresh_ValueChanged(object sender, EventArgs e)
		{
			tmrEventsRefresh.Interval = (int)nudEventsRefresh.Value * 60 * 1000;
		}

		private void refreshAccountsListing()
		{
			// clear
			lstAccounts.Items.Clear();

			// insert
			lstAccounts.Items.AddRange(mySettings.Accounts.ToArray());

			// select
			if (lstAccounts.Items.Count > 0)
			{
				lstAccounts.SelectedIndex = 0;
			}
		}

		private void tabMain_SelectedIndexChanged(object sender, EventArgs e)
		{
			if ((tabMain.SelectedTab == tbpFailedMail) && (updateServiceStatus()) && (lblStatusStatus.Text == "Running"))
			{
				MessageBox.Show("Changes to the Failed Emails will not take effect if the service is running", "Please stop service", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}

			if ((tabMain.SelectedTab == tbpAccounts) || (tabMain.SelectedTab == tbpFailedMail))
			{
				refreshAccountsListing();
				if (tabMain.SelectedTab == tbpAccounts)
				{
					lstAccounts_SelectedIndexChanged(lstAccounts, new EventArgs());
				}
			}
		}

		private void lstEventsList_DoubleClick(object sender, EventArgs e)
		{
			if (lstEventsList.SelectedItems.Count > 0)
			{
				string eventType = lstEventsList.SelectedItems[0].ImageKey.Substring(6, lstEventsList.SelectedItems[0].ImageKey.Length - 10);
				eventType = eventType.Substring(0, 1).ToUpper() + eventType.Substring(1);
				string source = lstEventsList.SelectedItems[0].SubItems[1].Text;
				string timeOccured = lstEventsList.SelectedItems[0].SubItems[0].Text;
				string message = lstEventsList.SelectedItems[0].SubItems[2].Text;

				frmViewEvent myEventViewer = new frmViewEvent(eventType, source, timeOccured, message);
				myEventViewer.Show();
				myEventViewer.FormClosed += new FormClosedEventHandler(myEventViewer_FormClosed);
			}
		}

		void myEventViewer_FormClosed(object sender, FormClosedEventArgs e)
		{
			((frmViewEvent)sender).Dispose();
		}

		private void btnAccountNewDefault_Click(object sender, EventArgs e)
		{
			frmAccount newAccountForm = new frmAccount(new Account(mySettings.DefaultAccount), false);

			if (newAccountForm.ShowDialog() == DialogResult.OK)
			{
				mySettings.Accounts.Add(newAccountForm.MyAccount);
				lstAccounts.Items.Add(newAccountForm.MyAccount);
				markUnsaved();
			}

			newAccountForm.Dispose();
		}

		private void btnAccountDefaultMake_Click(object sender, EventArgs e)
		{
			if (lstAccounts.SelectedIndices.Count == 1)
			{
				mySettings.DefaultAccount = new Account((Account)lstAccounts.SelectedItem);
			}
		}

		private void btnAccountDefaultDelete_Click(object sender, EventArgs e)
		{
			mySettings.DefaultAccount = new Account();
			markUnsaved();
		}

		private void btnAccountDefaultEdit_Click(object sender, EventArgs e)
		{
			frmAccount editAccountForm = new frmAccount(new Account(mySettings.DefaultAccount), true);

			if (editAccountForm.ShowDialog() == DialogResult.OK)
			{
				mySettings.DefaultAccount = editAccountForm.MyAccount;
				markUnsaved();
			}

			editAccountForm.Dispose();
		}

		private void btnAccountImportCSV_Click(object sender, EventArgs e)
		{
			if (ofdOpenCsv.ShowDialog() == DialogResult.OK)
			{
				mySettings.Accounts.AddRange(CsvImporter.ImportFromCSV(ofdOpenCsv.FileName, mySettings.DefaultAccount));
				markUnsaved();
				refreshAccountsListing();
			}
		}

		private void btnAccountImportCSVBlank_Click(object sender, EventArgs e)
		{
			if (ofdOpenCsv.ShowDialog() == DialogResult.OK)
			{
				mySettings.Accounts.AddRange(CsvImporter.ImportFromCSV(ofdOpenCsv.FileName, new Account()));
				markUnsaved();
				refreshAccountsListing();
			}
		}

		private void btnFailedMailAccountOpen_Click(object sender, EventArgs e)
		{
			bool cancelled = false;

			if ((updateServiceStatus()) && (srvcController.Status != ServiceControllerStatus.Stopped))
			{
				if (MessageBox.Show("Stop service?", "Failed emails can only be managed if the server is not running.\r\nStop the service and continue to manage failed emails?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
				{
					cancelled = true;
				}
				else
				{
					changeServiceStatus(ServiceAction.Stop);
				}
			}

			if (!cancelled)
			{
				string[] smtpServers = (from t in mySettings.Accounts
									   select t.SmtpAccount.ToShortString()).Distinct().ToArray();

				FailedMailEngine failedMail = FailedMailEngine.LoadFailedMail();
				frmFailedMail mailForm = new frmFailedMail(new List<FailedMailItem>(failedMail.MailItems), smtpServers);

				if (mailForm.ShowDialog() == DialogResult.OK)
				{
					failedMail.MailItems = mailForm.MailItems;
					FailedMailEngine.SaveFailedMail(failedMail);
				}

				mailForm.Dispose();
			}
		}

		private void btnCheckMail_Click(object sender, EventArgs e)
		{
			IServiceChannel channel = Connector.OpenService();

			if (channel == null)
			{
				MessageBox.Show("Unable to connect to service.", "Unable to connect", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			else
			{
				channel.CheckMailNow();
				Connector.CloseService(channel);
			}
		}
	}
}
